package com.itheima.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.common.ReggieConstants;
import com.itheima.dto.DishDto;
import com.itheima.exception.BizException;
import com.itheima.mapper.CategoryMapper;
import com.itheima.mapper.DishMapper;
import com.itheima.pojo.*;
import com.itheima.service.DishFlavorService;
import com.itheima.service.DishService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)

public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    // 由于mapper没有封装批量新增的方法 所以使用service
    @Autowired
    private DishFlavorService FlavorService;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public void addDishWithFlavor(DishDto dishDto) {
        // 储存菜品
        this.save(dishDto);     // 核心代码
        // 要记录菜品和菜品口味的关系 也就是所谓的dishId
        // 我们只要调用了sava方法之后 id会自动放到对象里
        Long dishId = dishDto.getId();
        // 增强for遍历
//        for (DishFlavor flavor : dishDto.getFlavors()) {
//            flavor.setDishId(dishId);
//        }
        if (dishDto.getFlavors()!= null && dishDto.getFlavors().size()>0) {
            // 流试
            dishDto.getFlavors().forEach(f -> {
                f.setDishId(dishId);
            });
            // 存储菜品口味
            FlavorService.saveBatch(dishDto.getFlavors());  // 核心代码
        }
        // 说明已经存储完毕
        // 可以开始删除缓存了
        // 把这个放在最下面 防止上面报错
        //由于我们还要处理namez这个参数,所以我们没办法直接按照分类来删除
        Set<String> keys = redisTemplate.keys( ReggieConstants.DISH_DTO_LIST_REDIS_KEY+"*");
        redisTemplate.delete(keys);
    }

    @Override
    public Page<DishDto> pageDish(Integer page, Integer pageSize, String name) {
        Page<Dish> dishPage = new Page<>(page,pageSize);
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        lqw.like(StringUtils.isNotBlank(name),Dish::getName,name);
        // 修改时间排序
        lqw.orderByDesc(Dish::getUpdateTime);
        this.page(dishPage,lqw);
        // 至此 dishPage 已经有数据了
        // 接下来把dishPage转为Page<DishDto>的格式
        Page<DishDto> dtoPage = new Page<>();
        // 对象拷贝
        BeanUtils.copyProperties(dishPage,dtoPage);
        if (dishPage.getRecords()==null || dishPage.getRecords().size()==0){
            return dtoPage;
        }
        // 复制过来的records记录 不要 先放个空的
        List<DishDto> dtoList = new ArrayList<>();  // 空的
        dtoPage.setRecords(dtoList);
        // 把dtoList生成出来
        // 遍历dish的list,把对象考位过来

        // 原始方法  查询数据库在循环中影响性能
//        dishPage.getRecords().forEach(dish -> {
//            // 一个一个拷贝到dtolist
//            DishDto dishDto = new DishDto();
//            BeanUtils.copyProperties(dish,dishDto);
//            Category category = categoryMapper.selectById(dish.getCategoryId());
//            dishDto.setCategoryName(category.getName());
//            dtoList.add(dishDto);
//        });

        //优化过后的方法
        // selete * from category where id in (?,?,?)
        // 原始获取id集合的办法
//        List<Long> categoryIds = new ArrayList<>();
//        dishPage.getRecords().forEach(dish -> {
//            categoryIds.add(dish.getCategoryId());
//        });
        // stream流的map可以只取其中一个字段
        // Set去重 
        Set<Long> categoryIdSet = dishPage.getRecords().stream().map(Dish::getCategoryId).collect(Collectors.toSet());
        List<Category> categoryList = categoryMapper.selectBatchIds(categoryIdSet);
        // 双for
        // 外层循环
        for (Dish dish : dishPage.getRecords()) {

            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(dish,dishDto);
            dtoList.add(dishDto);
            // 内层循环
            for (Category category : categoryList) {
                if (dishDto.getCategoryId().equals(category.getId())){
                //  匹配上了
                    // 赋值CategoryName
                dishDto.setCategoryName(category.getName());}
            }
        }

        // stream流
//        dishPage.getRecords().forEach(dish -> {
//            DishDto dishDto = new DishDto();
//            BeanUtils.copyProperties(dish,dishDto);
//            dtoList.add(dishDto);
//            categoryList.stream().filter(cate->cate.getId().equals(dish.getCategoryId())).findAny().ifPresent(cate->{
//                // 代表匹配上了
//                dishDto.setCategoryName(cate.getName());
//            });
//        });

        // 转map
        //key--->category的id value就是他自己
        // list转map
//        Map<Long,Category> categoryMap = categoryList.stream().collect(Collectors.toMap(Category::getId, Function.identity()));
////
////        for循环转  等价与下面
////        for (Category category : categoryList){
////            categoryMap.put(category.getId(),category);
////        }
//        dishPage.getRecords().forEach(dish -> {
//            DishDto dishDto = new DishDto();
//            BeanUtils.copyProperties(dish,dishDto);
//            dtoList.add(dishDto);
//            // 为了防止map取出来的是个null , 可以使用getOrDefault
//            Category category = categoryMap.get(dish.getCategoryId());
//            // 如果dish.getCategoryId取出来的是个null就用后面这个new Category 防止空指针
//            categoryMap.getOrDefault(dish.getCategoryId(), new Category());
//            dishDto.setCategoryName(category.getName());
//        });
         // 直接写sql
            // select * from dish as d left join category as c on d.category_id = c.id
        return dtoPage;
    }

    @Override
    public DishDto getDishWithFlavor(Long id) {
        Dish dish = this.getById(id);
        // 拷贝
        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish,dishDto);
        // 拷贝完了 还剩口味
        // sql
        // select * from dish_flav where dish_id = ?
        List<DishFlavor> flavorList =FlavorService
                .list(new LambdaQueryWrapper<DishFlavor>()
                        .eq(DishFlavor::getDishId,id ));
        dishDto.setFlavors(flavorList);

        return dishDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDishWithFlavor(DishDto dishDto) {
        // 修改菜品
        this.updateById(dishDto);
        // 修改口味
        // 由于口味是个list
        // 所以直接暴力接触  全部删除 再全部添加
        // 前提里面数据和其他表没有关联
        // delect * from dish_flavor where dish_id = ?
//        LambdaQueryWrapper<DishFlavor> lqw = new LambdaQueryWrapper();
//        lqw.eq(dishDto.getId()!=null,DishFlavor::getDishId,dishDto.getId());
//        this.FlavorService.remove(lqw);
         this.FlavorService.remove(new LambdaQueryWrapper<DishFlavor>()
                 .in(DishFlavor::getDishId,dishDto.getId()));
        // 再新增

        dishDto.getFlavors().forEach(f->{
            f.setDishId(dishDto.getId());
        });
        this.FlavorService.saveBatch(dishDto.getFlavors());
        //TODO 还要去修改购物车l里的数据
        Set<String> keys = redisTemplate.keys( ReggieConstants.DISH_DTO_LIST_REDIS_KEY+"*");
        redisTemplate.delete(keys);
    }

    @Override
    public List<DishDto> getDishList(Long categoryId, String name , Integer status) {
        //  先去查询缓存 如果缓存已经有值了 那就返回了
        String redisKey = ReggieConstants.DISH_DTO_LIST_REDIS_KEY +categoryId+":"+name+":"+status;
        List<DishDto> cacheList = (List<DishDto>) redisTemplate.opsForValue().get(redisKey);
        if (cacheList!=null){
            // 缓存中有这条数据
            return cacheList;
        }

        // 如果没有缓存呢?
        //select * from dish where categoryId = ? and name like ? order by updateTime
        LambdaQueryWrapper<Dish> lqw = new LambdaQueryWrapper<>();
        lqw.eq(categoryId!=null,Dish::getCategoryId,categoryId);
        lqw.like(StringUtils.isNotBlank(name),Dish::getName,name);
        lqw.eq(status!=null, Dish::getStatus,status);
        lqw.orderByDesc(Dish::getUpdateTime);
        // 拿到实体类的list
         List<Dish> dishList = this.list(lqw);

         // 提前查出来这个菜品list对应的口味信息
        //dishID的集合
        // sql: select * from dish_flavor where dish_id in (?,?,?)
        Set<Long> dishIdSet = dishList.stream().map(Dish::getId).collect(Collectors.toSet());
        List<DishFlavor> flavorList = FlavorService.list(new LambdaQueryWrapper<DishFlavor>()
                    .in(DishFlavor::getDishId,dishIdSet));
        Map<Long,List<DishFlavor>> flavorMap = flavorList.stream().collect(Collectors.groupingBy(DishFlavor::getDishId));
         // 构造这个List<dishDto>

        // 1 双重for
        List<DishDto> dtoList = new ArrayList<>();
       dishList.forEach(dish->{
            DishDto dto = new DishDto();
            BeanUtils.copyProperties(dish,dto);
            dtoList.add(dto);
//            List<DishFlavor> flavorList = FlavorService.list(new LambdaQueryWrapper<DishFlavor>()
//                    .eq(DishFlavor::getDishId, dish.getId()));
//            dto.setFlavors(flavorList);
            // 由于菜品和菜品属性是1对多的 所以他的属性是list
           //初始化口味list
           List<DishFlavor> flavors = new ArrayList<>();
            dto.setFlavors(flavors);
            flavorList.forEach(f->{
               if (f.getDishId().equals(dish.getId())){
                   // 匹配上了
//                   List<DishFlavor> flavors = dto.getFlavors();
                   flavors.add(f);
              }
           });
        });

        // 2 setream流
//        dishList.forEach(dish-> {
//            DishDto dto = new DishDto();
//            BeanUtils.copyProperties(dish, dto);
//            dishList.add(dto);
//            // 拿到匹配上的口味list
//            List<DishFlavor> flavor = flavorList.stream()
//                    .filter(f -> f.getDishId().equals(dish.getId())).collect(Collectors.toList());
//            dto.setFlavors(flavor);
//        });

        // 3转成map
//        dishList.forEach(dish-> {
//            DishDto dto = new DishDto();
//            BeanUtils.copyProperties(dish, dto);
//            dishList.add(dto);
//            // 直接通过map来拿
//            List<DishFlavor> flavors = flavorMap.get(dish.getId());
//            dto.setFlavors(flavors);
//        });

        redisTemplate.opsForValue().set(redisKey,dtoList,30, TimeUnit.MINUTES);
        return dtoList;
    }

    // 删除菜品
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeDishList(List<Long> ids) {
        // select count(*) from  Dish where status = 0 and id in(?,?,?)
        // 查询状态 是否停售
        LambdaQueryWrapper<Dish> lqm = new LambdaQueryWrapper<>();
        int count = this.count(lqm.eq(Dish::getStatus,1).in(Dish::getId,ids));
        if (count>0){
            throw new BizException("套餐在售中 无法删除");
        }

        // 走到这里说明可以删除
        this.removeByIds(ids);
        // 删除表中口味
        //delete from dish_flavor where Dish_id in (?,?,?)
        // LambdaQueryWrapper<DishFlavor> lqm = new LambdaQueryWrapper<>();
        FlavorService.remove(new LambdaQueryWrapper<DishFlavor>()
                .in(DishFlavor::getDishId,ids));


    }

    // 批量启动
    @Override
    public void updateDish(List<Long> ids) {
        // update Dish set status = 1  where id in (?,?,?)
        LambdaUpdateWrapper<Dish> lqw = new LambdaUpdateWrapper<>();
        lqw.set(Dish::getStatus,1).in(Dish::getId, ids);
        this.update(lqw);
    }

    // 批量停售
    @Override
    public void statusDish0(List<Long> ids) {
        // update Dish set status = 0  where id in (?,?,?)
        LambdaUpdateWrapper<Dish> lqw = new LambdaUpdateWrapper<>();
        lqw.set(Dish::getStatus, 0).in(Dish::getId, ids);
        this.update(lqw);
    }

}
